﻿/******************************************************************************
 * Copyright (C) 671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "AuthSocketDelegate.h"
#include <cassert>
#include <NodeCpp/Platform.h>
#include <NodeCpp/AutoPtr.h>
#include "Shared/Log/Log.h"
#include "Shared/Auth/AuthCode.h"
#include "Shared/Auth/AuthPacket.h"
#include "AuthSessionManager.h"
#include "AuthSession.h"
using NodeCpp::AutoPtr;

AuthSocketDelegate::AuthSocketDelegate(void)
    : Session_(NULL)
    , PacketProcessing_(false)
    , Closing_(false)
{

}

AuthSocketDelegate::~AuthSocketDelegate(void)
{

}

void AuthSocketDelegate::OnOpen(void)
{
    /* 打开连接时创建验证会话 */
    if (!(Session_ = sAuthSessionMgr.NewSession(this))) {
        peer().Close();
        return;
    }

    sLog.outDebug("Connection(%d) OPENED from %s:%d",
        peer().id(), peer().host().c_str(), peer().port());
}

void AuthSocketDelegate::OnRead(void)
{
    if (Closing_) {
        return;
    }

    PacketProcessing_ = true;

    //sLog.outDebug("Connection(%d) RECEIVED(%d) bytes",
    //    peer().id(), peer().GetReceivedSize());

    bool _Error = false;
    std::size_t _ReceivedSize = peer().GetReceivedSize();

    while (_ReceivedSize > 0)
    {
        std::uint8_t _Cmd;
        peer().ReadPeek(&_Cmd, 1);

        std::size_t _PktSize = 0;

        if (_Cmd == CMD_AUTH_LOGON_CHALLENGE || _Cmd == CMD_AUTH_RECONNECT_CHALLENGE)
        {
            if (_ReceivedSize < sizeof(sAuthLogonChallenge_C)) {
                break;
            }

            std::uint8_t _Header[4];
            peer().ReadPeek(_Header, sizeof(_Header));

#ifdef ENDIAN_BIG
            NodeCpp::EndianConvert(*((std::uint16_t*)(_Header[0])));
#endif

            _PktSize = sizeof(_Header);
            sAuthLogonChallenge_C* _PktDef = (sAuthLogonChallenge_C*)_Header;
            _PktSize += _PktDef->Size;

            if (_ReceivedSize < _PktSize) {
                break;
            }
        }
        else if (_Cmd == CMD_AUTH_LOGON_PROOF)
        {
            _PktSize = sizeof(sAuthLogonProof_C);
            if (_ReceivedSize < _PktSize) {
                break;
            }
        }
        else if (_Cmd == CMD_AUTH_RECONNECT_PROOF)
        {
            _PktSize = sizeof(sAuthReconnectProof_C);
            if (_ReceivedSize < _PktSize) {
                break;
            }
        }
        else if (_Cmd == CMD_REALM_LIST)
        {
            _PktSize = 5;
            if (_ReceivedSize < _PktSize) {
                break;
            }
        }
        else {
            _Error = true;
            break;
        }

        AutoPtr<AuthPacket> _AuthPkt(new AuthPacket(_Cmd, _PktSize));
        if (_AuthPkt) {
            _AuthPkt->Resize(_PktSize);
            NODECPP_ASSERT(peer().Read(_AuthPkt->contents(), _PktSize) == int(_PktSize));
            _ReceivedSize -= _PktSize;
            Session_->QueuePacket(_AuthPkt.release());
        } else {
            sLog.outError("failed malloc AuthPacket");
            _Error = true;
            break;
        }
    }

    PacketProcessing_ = false;

    if (_Error) {
        Closing_ = true;
    }
}

void AuthSocketDelegate::OnWrite(void)
{

}

void AuthSocketDelegate::OnEof(void)
{
    sLog.outDebug("Connection(%d) EOF", peer().id());

    Closing_ = true;
}

void AuthSocketDelegate::OnClose(void)
{
    sLog.outDebug("Connection(%d) CLOSED", peer().id());
}

void AuthSocketDelegate::OnError(void)
{
    sLog.outDebug("Connection(%d) ERROR, %d, %s",
        peer().id(), peer().GetErrorCode(), peer().GetErrorString().c_str());

    Closing_ = true;
}

void AuthSocketDelegate::OnTimeout(int)
{

}

bool AuthSocketDelegate::TryClose(void)
{
    Closing_ = true;

    if (!PacketProcessing_) {
        peer().Close();
        return true;
    }
    return false;
}
